/*
 * Copyright (c) 2007, TopCoder, Inc. All rights reserved.
 */
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace TopCoder.WinForms.Controls.CalendarView
{
    /// <summary>
    /// <para>This control holds buttons used for navigation of the different calendar view types.The
    /// <see cref="CalendarViewControl"/> contains an instance of this control, used to navigate the different
    /// calendar view types. This class contains a <see cref="CalendarViewControl"/> instance, used to change
    /// the view, based on what button was clicked. This class contains additional functionality for adding
    /// different buttons and additional buttons, if the user requires a custom layout. Otherwise the default
    /// is used.</para>
    /// </summary>
    /// <remarks>Thready Safety: This control is mutable and is not thread safe.</remarks>
    /// <author>Ghostar</author>
    /// <author>yext</author>
    /// <author>argolite</author>
    /// <author>alex.dolin</author>
    /// <version>1.1</version>
    /// <copyright>Copyright (c) 2007, TopCoder, Inc. All rights reserved.</copyright>
    public class CalendarViewNavigationControl : UserControl
    {
        /// <summary>
        /// <para>This member variable is a pointer back to the parent control, used in the <see cref="OnButtonClick"/>
        /// method to change the view. The <see cref="CalendarViewControl.CurrentViewType"/> property is used to
        /// change the view type to what the user selected, for example "Daily" or "Yearly". This value is set
        /// in the constructor and can't change after instantiation. This value can't be null.</para>
        /// </summary>
        private readonly CalendarViewControl parent;

        /// <summary>
        /// <para>This <c>IDictionary</c> maps <c>Buttons</c> to string type descriptions. The
        /// string type descriptions should match those in the <c>CalendarViewControl.views</c>
        /// collection, so that when a specific button is pressed, we can call <c>parent.CurrentViewType</c>
        /// to set the new selected view. This value is used in <see cref="OnButtonClick"/> to get the view
        /// description string associated with the button that was pressed. This collection is intialized to
        /// a <c>Dictionary</c> instance in the constructor and is initially filled with the default
        /// buttons for the 5 default calendar view types. The keys in this collection can't be null, and the
        /// values can't be null or empty strings.</para>
        /// </summary>
        private readonly IDictionary<Button, string> viewTypes = new Dictionary<Button, string>();

        /// <summary>
        /// <para>This private helper method creates a new button with the parameter <paramref name="type"/>
        /// as the text, and calls <see cref="AddButton"/> method to add it into the <see cref="Controls"/>
        /// collection.</para>
        /// </summary>
        /// <param name="type">The calendar type description string associated with the button.</param>
        private void AddPredefinedButton(string type)
        {
            Button button = new Button();
            button.Text = type;
            AddButton(button, type);
        }

        /// <summary>
        /// <para>This is the constructor for this class.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If the given parameter is <c>null</c>.</exception>
        /// <param name="parent">The parent <see cref="CalendarViewControl"/>.</param>
        public CalendarViewNavigationControl(CalendarViewControl parent)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent", "Argument parent should not be null.");
            }

            this.parent = parent;

            AddPredefinedButton("Day");
            AddPredefinedButton("Week");
            AddPredefinedButton("Month");
//            AddPredefinedButton("Quarter");
//            AddPredefinedButton("Year");

            Resize += new EventHandler(OnResize);
        }

        /// <summary>
        /// <para>This method adds a new button to the far right of the navigation panel.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If either parameter is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If the type string is an empty string.</exception>
        /// <param name="button">The button to add to the control</param>
        /// <param name="type">The calendar type description string associated with the button.</param>
        public void AddButton(Button button, string type)
        {
            if (button == null)
            {
                throw new ArgumentNullException("button", "Argument button should not be null.");
            }

            if (type == null)
            {
                throw new ArgumentNullException("type", "Argument type should not be null.");
            }
            if (type.Trim().Length == 0)
            {
                throw new ArgumentException("Argument type should not be empty.", "type");
            }

            if (!viewTypes.ContainsKey(button))
            {
                button.Dock = DockStyle.Right;
                Controls.Add(button);
            }

            viewTypes[button] = type;

            button.Click += new EventHandler(OnButtonClick);
        }

        /// <summary>
        /// <para>This method removes the <c>Button</c>(s) with the specified type name from the control.</para>
        /// <para>This method allows the user to customize the buttons offered for navigation, so the user can
        /// remove the predefined buttons.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If <paramref name="type"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="type"/> is an empty string.</exception>
        /// <param name="type">The type of view to remove button(s) for.</param>
        public void RemoveButton(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type", "Argument type should not be null.");
            }
            if (type.Trim().Length == 0)
            {
                throw new ArgumentException("Argument type should not be empty.", "type");
            }

            KeyValuePair<Button, string>[] pairs = new KeyValuePair<Button, string>[viewTypes.Count];
            viewTypes.CopyTo(pairs, 0);

            for (int i = 0; i < pairs.Length; i++)
            {
                if (pairs[i].Value == type)
                {
                    viewTypes.Remove(pairs[i].Key);
                    Controls.Remove(pairs[i].Key);
                }
            }
        }

        /// <summary>
        /// <para>This method handles button presses for each button on the navigation control.</para>
        /// </summary>
        /// <exception cref="CalendarViewControlException">If errors occur while handling the button click event.
        /// </exception>
        /// <param name="sender">The button that was pressed.</param>
        /// <param name="e">The arguments for the event.</param>
        private void OnButtonClick(object sender, EventArgs e)
        {
            try
            {
                parent.CurrentViewType = viewTypes[sender as Button];
            }
            catch (Exception ex)
            {
                throw new CalendarViewControlException(
                    "Errors occur while handling the button click event.", ex);
            }
        }

        /// <summary>
        /// <para>This method handles redrawing the buttons when the control is resized.</para>
        /// <para>This method makes sure the buttons fill in equal widths to fill the resized control.</para>
        /// </summary>
        /// <param name="sender">The sender of the event (ignored by this method).</param>
        /// <param name="e">The event args (ignored by this method).</param>
        private void OnResize(object sender, EventArgs e)
        {
            int remainWidth = Width;
            int remain = viewTypes.Keys.Count;
            foreach (Button button in viewTypes.Keys)
            {
                button.Width = remainWidth / remain;
                remainWidth -= button.Width;
                remain--;
            }
        }
    }
}
